#include "ChessBoard.h"

//std::string ChessBoard::m_debugStr = "";

ChessBoard::ChessBoard(void)
	:m_CurrPlayer(White)
	,m_BlackKingSide(Disable)
	,m_BlackQueenSide(Disable)
	,m_WhiteKingSide(Disable)
	,m_WhiteQueenSide(Disable)
	,m_bCanEnPassant(false)
	,m_bGameOver(false)
	,m_Winner(0)
{
}


ChessBoard::~ChessBoard(void)
{
}

//void ChessBoard::ResetCtrlScope()
//{
//	std::memset(m_WhiteCtrlScope,false,sizeof(m_WhiteCtrlScope));
//	std::memset(m_BlackCtrlScope,false,sizeof(m_BlackCtrlScope));
//}

void ChessBoard::Reset()
{
	ResetBoard();
	ResetBoardState();
	//ResetCtrlScope();
}


void ChessBoard::ResetBoard()
{
	for(int i=0 ;i<ChessBoardSize ;++i)
		for(int j=0 ;j<ChessBoardSize ;++j)
			m_Board[i][j] = Invaild;

	int rowBegin = RowBeginIndex;
	int rowEnd = ChessBoardSize - RowBeginIndex;
	int colBegin = rowBegin;
	int colEnd = rowEnd;
	//set the empty grid
	for(int i=rowBegin+2 ;i<rowEnd-2 ;++i)
		for(int j=colBegin ;j<colEnd ;++j)
			m_Board[i][j] = Empty;

	//sign represent the white and black

	//set the white chessman
	for(int i=colBegin ;i<colEnd ;++i)
	{
		m_Board[rowBegin+1][i] = Pawn;
	}
	m_Board[rowBegin][colBegin] = m_Board[rowBegin][colEnd-1] = Rook;
	m_Board[rowBegin][colBegin+1] = m_Board[rowBegin][colEnd-2] = Knight;
	m_Board[rowBegin][colBegin+2] = m_Board[rowBegin][colEnd-3] = Bishop;
	m_Board[rowBegin][colBegin+3] = Queen;
	m_Board[rowBegin][colBegin+4] = King;

	//set the black chessman
	for(int j=colBegin ;j<colEnd ;++j)
	{
		m_Board[rowEnd-1][j] = -m_Board[rowBegin][j];
		m_Board[rowEnd-2][j] = -m_Board[rowBegin+1][j];
	}
}


void ChessBoard::ResetBoardState()
{
	m_CurrPlayer = White;
	m_BlackKingSide = Disable;
	m_BlackQueenSide = Disable;
	m_WhiteKingSide = Disable;
	m_WhiteQueenSide = Disable;
	m_bCanEnPassant = false;
}


bool ChessBoard::LegalMove( const ChessManMoveInfo& info ) const
{
	//has no legal direction
	if(info.step == 0 || !HasLegalDir(info))
	{
		//m_debugStr = "error! HasLegalDir()  "
		//	+ ChessManNameTab[abs(m_Board[info.chessManCoord.x + RowBeginIndex][info.chessManCoord.y + ColBeginIndex])];
		return false;
	}
	//get the chessman's type
	int chessManType = m_Board[info.chessManCoord.x + RowBeginIndex][info.chessManCoord.y + ColBeginIndex];

	//if it's not this player's turn,then return false
	if(m_CurrPlayer * chessManType < 0)
	{
		//m_debugStr = "exit ! player error" 
		//	+ ChessManNameTab[abs(chessManType)];
		return false;
	}

	if( abs(chessManType) == Pawn )
	{
		//pawn need special process
		//it can't go backward
		//if no capture ,it can't move in y coordinate
		//it can move 2 step only when it in it's origin position

		if(chessManType == Pawn)
		{
			//white pawn
			//it can move 2 step only when it in it's origin position
			if(info.step==2 && info.chessManCoord.x != 1)
				return false;
		}//end if
		else
		{
			//black pawn
			//it can move 2 step only when it in it's origin position
			if(info.step==2 && info.chessManCoord.x+RowBeginIndex != ChessBoardSize-RowBeginIndex-2)
				return false;
		}//end else

		bool bCanCapture = CanCapture(info);
		//it can move in y axis only it can have capture
		if(info.y != 0)
		{
			if(!bCanCapture && !CanEnPassant(info))
				return false;
		}
		else
		{
			//if y == 0 ,then it can't has capture
			if(bCanCapture)
				return false;
		}
	}//end else

	//the castle also need special process
	if(abs(chessManType) == King && info.step == 2)
	{
		if(!CanCastle(info))
		{
			//m_debugStr = "exit! CanCastle()"
			//	+ ChessManNameTab[abs(chessManType)];
			return false;
		}
	}

	//detect the out of chessboard and the obstructed,return false if nothing has be detected
	if(DetectOverstepAndObstructed(info))
	{
		//m_debugStr = "error! DetectOverstepAndObstructed()" + ChessManNameTab[abs(chessManType)];
		return false;
	}
	//m_debugStr = "no error";
	//a legal move ,return true
	return true;
}

bool ChessBoard::MoveChessMan( const ChessManMoveInfo& info )
{
	if(!LegalMove(info))
		return false;

	BoardCoord source,dest;
	source.x = info.chessManCoord.x + RowBeginIndex;
	source.y = info.chessManCoord.y + ColBeginIndex;

	dest.x = source.x + info.x*info.step;
	dest.y = source.y + info.y*info.step;

	//update the board update info
	UpdateMoveInfo(info,source,dest);

	if( abs(m_Board[source.x][source.y])==King && info.step==2 )
	{
		//castle, we need to move the rook
		MoveRook(info);
	}

	SetCastleState(info);

	m_Board[dest.x][dest.y] = m_Board[source.x][source.y];
	m_Board[source.x][source.y] = Empty;

	//push the update information into stack for retract one move
	//m_MoveInfoStack.push(m_ChessBoardUpdateInfo);

	//change current player
	m_CurrPlayer = -m_CurrPlayer;

	return true;
}

bool ChessBoard::CanCapture( const ChessManMoveInfo& info ) const
{
	BoardCoord source;
	source.x = info.chessManCoord.x + RowBeginIndex;
	source.y = info.chessManCoord.y + ColBeginIndex;

	BoardCoord dest;
	dest.x = source.x + (info.x * info.step);
	dest.y = source.y + (info.y * info.step);

	if(m_Board[dest.x][dest.y] == Invaild)
		return false;

	if(m_Board[source.x][source.y] * m_Board[dest.x][dest.y] < 0)
		return true;

	return false;
}

bool ChessBoard::HasLegalDir( const ChessManMoveInfo& info ) const
{
	//get the chessman's type
	int chessManType = m_Board[info.chessManCoord.x + RowBeginIndex][info.chessManCoord.y + ColBeginIndex];

	//get it's move table
	std::vector<ChessManMoveDir>* pMoveTab = ChessManMoveTable::Instance()->GetChessManMoveTab( abs(chessManType) );
	std::vector<ChessManMoveDir>::const_iterator iterMoveTab;

	//find a legal direction,and the step <= iMaxStep,if not exist then return false
	if(chessManType > 0)
	{
		for(iterMoveTab=pMoveTab->begin() ;iterMoveTab!=pMoveTab->end() ;++iterMoveTab)
		{
			if(info.x == iterMoveTab->x && info.y == iterMoveTab->y 
				&& info.step <= iterMoveTab->iMaxStep)
				return true;
		}//end for
	}//end if
	else if (chessManType < 0)
	{
		//black player
		for(iterMoveTab=pMoveTab->begin() ;iterMoveTab!=pMoveTab->end() ;++iterMoveTab)
		{
			if(-info.x == iterMoveTab->x && -info.y == iterMoveTab->y 
				&& info.step <= iterMoveTab->iMaxStep)
				return true;
		}//end for
	}//end if

	return false;
}

bool ChessBoard::CanEnPassant( const ChessManMoveInfo& info ) const
{
	BoardCoord source,dest;
	source.x = info.chessManCoord.x + RowBeginIndex;
	source.y = info.chessManCoord.y + ColBeginIndex;

	dest.x = m_EnPassant.x + RowBeginIndex;
	dest.y = m_EnPassant.y + ColBeginIndex;

	if( !m_bCanEnPassant || abs(m_Board[source.x][source.y]) != Pawn )
		return false;

	//they must have the same x value
	if(source.x != dest.x)
		return false;

	//they must close to each other
	if( abs(source.y - dest.y) != 1 )
		return false;

	////if they have the same color
	//if(m_Board[source.x][source.y] * m_Board[dest.x][dest.y] > 0)
	//	return false;

	//after En Passant,they must has the same y value
	if(source.y + info.y != dest.y)
		return false;

	return true;
}

bool ChessBoard::DetectOverstepAndObstructed( const ChessManMoveInfo& info ) const
{
	//get the chessman's type
	int chessManType = m_Board[info.chessManCoord.x + RowBeginIndex][info.chessManCoord.y + ColBeginIndex];

	//if the chessman out of the chessboard or obstructed by another chessman then return false
	BoardCoord dest;
	dest.x = info.chessManCoord.x + RowBeginIndex;
	dest.y = info.chessManCoord.y + ColBeginIndex;

	short compareResult;
	for(short i=1 ;i<=info.step ;++i)
	{
		dest.x += info.x;
		dest.y += info.y;

		//out of chessboard
		if(m_Board[dest.x][dest.y] == Invaild)
			return true;

		compareResult = m_Board[dest.x][dest.y] * chessManType;
		//obstructed by same color chessman
		if(compareResult > 0)
			return true;
		else if(compareResult < 0 && i!=info.step)
		{
			//obstructed by opponent's chessman but the step isn't the last step
			return true;
		}

	}//end for

	return false;
}

int ChessBoard::GetChessManType( const BoardCoord& coord ) const
{
	return m_Board[coord.x + RowBeginIndex][coord.y + ColBeginIndex];
}

//bool ChessBoard::RetractOneMove()
//{
//	//if(m_MoveInfoStack.empty())
//		//return false;
//	//change player
//	m_CurrPlayer = -m_CurrPlayer;
//
//	ChessBoardUpdateInfo info = m_MoveInfoStack.top();
//	m_MoveInfoStack.pop();
//
//	//recover the state
//	m_bGameOver = info.bGameOver;
//	m_WhiteKingSide = info.castleWhiteKingSide;
//	m_WhiteQueenSide = info.castleWhiteQueenSide;
//	m_BlackKingSide = info.castleBlackKingSide;
//	m_BlackQueenSide = info.castleBlackQueenSide;
//
//	m_bCanEnPassant = info.bCanEnPassant;
//	if(m_bCanEnPassant)
//		m_EnPassant = info.EnPassantCoord;
//
//	BoardCoord source,dest;
//	source.x = info.moveInfo.chessManCoord.x + RowBeginIndex;
//	source.y = info.moveInfo.chessManCoord.y + ColBeginIndex;
//
//	dest.x = source.x + info.moveInfo.x * info.moveInfo.step;
//	dest.y = source.y + info.moveInfo.y * info.moveInfo.step;
//
//
//	//because we want to retract one move,so we need move one chessman from dest to source
//	m_Board[source.x][source.y] = m_Board[dest.x][dest.y];
//	m_Board[dest.x][dest.y] = Empty;
//
//	if(info.castleMove != NoCastle)
//	{
//		RetractMoveRook(info);
//	}
//
//	if(info.bPawnPromotion)
//	{
//		//black pawn
//		if(dest.x == RowBeginIndex)
//			m_Board[source.x][source.y] = -Pawn;
//		//white pawn
//		else m_Board[source.x][source.y] = Pawn;
//	}
//
//	if(info.bHasCapture)
//	{
//		//add the captured chessman into chessboard
//		//m_Board[dest.x][dest.y] = info.captureChessManType;
//		m_Board[info.captureCoord.x+RowBeginIndex][info.captureCoord.y+ColBeginIndex] = info.captureChessManType;
//		return true;
//	}
//	return true;
//}

void ChessBoard::UpdateMoveInfo( const ChessManMoveInfo& info,const BoardCoord& source,const BoardCoord& dest )
{
	//update the board update info
	m_ChessBoardUpdateInfo.moveInfo = info;

	//note the special information
	m_ChessBoardUpdateInfo.bHasCapture = false;
	m_ChessBoardUpdateInfo.bPawnPromotion = false;

	//save current state
	m_ChessBoardUpdateInfo.bGameOver = m_bGameOver;
	m_ChessBoardUpdateInfo.castleMove = NoCastle;
	m_ChessBoardUpdateInfo.castleBlackKingSide = m_BlackKingSide;
	m_ChessBoardUpdateInfo.castleBlackQueenSide = m_BlackQueenSide;
	m_ChessBoardUpdateInfo.castleWhiteKingSide = m_WhiteKingSide;
	m_ChessBoardUpdateInfo.castleWhiteQueenSide = m_WhiteQueenSide;

	m_ChessBoardUpdateInfo.bCanEnPassant = m_bCanEnPassant;
	if(m_bCanEnPassant)
		m_ChessBoardUpdateInfo.EnPassantCoord = m_EnPassant;

	//note the capture information
	if(CanCapture(info))
	{
		m_ChessBoardUpdateInfo.bHasCapture = true;
		m_ChessBoardUpdateInfo.captureChessManType = m_Board[dest.x][dest.y];
		m_ChessBoardUpdateInfo.captureCoord.x = dest.x - RowBeginIndex;
		m_ChessBoardUpdateInfo.captureCoord.y = dest.y - ColBeginIndex;

		//king has be captured, find the winner
		if( abs(m_ChessBoardUpdateInfo.captureChessManType)==King )
		{
			if(m_ChessBoardUpdateInfo.captureChessManType < 0)
				m_Winner = White;
			else m_Winner = Black;
			m_bGameOver = true;
		}
	}//end if	
	else if(CanEnPassant(info))
	{
		//if it's an En Passant move
		m_ChessBoardUpdateInfo.bHasCapture = true;
		m_ChessBoardUpdateInfo.captureChessManType = m_Board[m_EnPassant.x+RowBeginIndex][m_EnPassant.y+ColBeginIndex];
		m_ChessBoardUpdateInfo.captureCoord = m_EnPassant;
	}

	//pawn need special process
	m_bCanEnPassant = false;

	if( abs(m_Board[source.x][source.y]) == Pawn )
	{
		//En Passant information
		if(info.step == 2)
		{
			m_bCanEnPassant = true;
			m_EnPassant.x = dest.x - RowBeginIndex;
			m_EnPassant.y = dest.y - ColBeginIndex;
		}

		//pawn promotion
		if(dest.x == RowBeginIndex)
		{
			//black pawn
			m_ChessBoardUpdateInfo.bPawnPromotion = true;
			//can't move chess in this method,so we let the pawn become queen in the source
			m_Board[source.x][source.y] = -Queen;
		}
		else if(dest.x == ChessBoardSize-RowBeginIndex-1)
		{
			//white pawn
			m_ChessBoardUpdateInfo.bPawnPromotion = true;
			//can't move chess in this method,so we let the pawn become queen in the source
			m_Board[source.x][source.y] = Queen;
		}
	}//end if
}

void ChessBoard::FindAllLegalMove( std::queue<ChessManMoveInfo>& infoQueue,int _playerType ) const
{
	int playerType = _playerType;
	int chessmanType;
	ChessManMoveInfo info;

	std::vector<ChessManMoveDir>* pMoveTab;
	std::vector<ChessManMoveDir>::const_iterator iterMoveTab;

	for(int i=0 ;i<8 ;++i)
	{
		info.chessManCoord.x = i;
		for(int j=0 ;j<8 ;++j)
		{
			info.chessManCoord.y = j;
			chessmanType = GetChessManType(info.chessManCoord);

			//it not it's own chessman
			if(playerType * chessmanType <= 0)
				continue ;

			//find this chessman all legal move and push it into queue
			pMoveTab = ChessManMoveTable::Instance()->GetChessManMoveTab( abs(chessmanType) );
			for(iterMoveTab=pMoveTab->begin(); iterMoveTab!=pMoveTab->end(); ++iterMoveTab)
			{
				info.x = iterMoveTab->x * playerType;
				info.y = iterMoveTab->y * playerType;
				for(int idxStep=1; idxStep<=iterMoveTab->iMaxStep; ++idxStep)
				{
					info.step = idxStep;
					if(LegalMove(info))
						infoQueue.push(info);
					else
					{
						//if this step isn't a legal move,continue increment
						//the step is still an illegal move,so here will break
						break;
					}//end else

				}//end for
			}//end for
		}//end for

	}//end for
}

void ChessBoard::MoveRook( const ChessManMoveInfo& info )
{
	if(m_CurrPlayer == White)
	{
		if(info.y == 1)
		{
			//king side castle
			m_Board[0+RowBeginIndex][7+ColBeginIndex] = Empty;
			m_Board[0+RowBeginIndex][5+ColBeginIndex] = Rook;
			m_ChessBoardUpdateInfo.castleMove = WhiteKingSide;
		}
		else
		{
			//queen side castle
			m_Board[0+RowBeginIndex][0+ColBeginIndex] = Empty;
			m_Board[0+RowBeginIndex][3+ColBeginIndex] = Rook;
			m_ChessBoardUpdateInfo.castleMove = WhiteQueenSide;
		}
	}
	else
	{
		if(info.y == 1)
		{
			//king side castle
			m_Board[7+RowBeginIndex][7+ColBeginIndex] = Empty;
			m_Board[7+RowBeginIndex][5+ColBeginIndex] = -Rook;
			m_ChessBoardUpdateInfo.castleMove = BlackKingSide;
		}
		else
		{
			//queen side castle
			m_Board[7+RowBeginIndex][0+ColBeginIndex] = Empty;
			m_Board[7+RowBeginIndex][3+ColBeginIndex] = -Rook;
			m_ChessBoardUpdateInfo.castleMove = BlackQueenSide;
		}
	}
}

//void ChessBoard::RetractMoveRook( const ChessBoardUpdateInfo& info )
//{
//	if(info.castleMove == WhiteKingSide)
//	{
//		m_Board[0+RowBeginIndex][7+ColBeginIndex] = Rook;
//		m_Board[0+RowBeginIndex][5+ColBeginIndex] = Empty;
//		return ;
//	}
//	if(info.castleMove == WhiteQueenSide)
//	{
//		m_Board[0+RowBeginIndex][0+ColBeginIndex] = Rook;
//		m_Board[0+RowBeginIndex][3+ColBeginIndex] = Empty;
//		return ;
//	}
//	if(info.castleMove == BlackKingSide)
//	{
//		m_Board[7+RowBeginIndex][7+ColBeginIndex] = -Rook;
//		m_Board[7+RowBeginIndex][5+ColBeginIndex] = Empty;
//		return ;
//	}
//	if(info.castleMove == BlackQueenSide)
//	{
//		m_Board[7+RowBeginIndex][0+ColBeginIndex] = -Rook;
//		m_Board[7+RowBeginIndex][3+ColBeginIndex] = Empty;
//		return ;
//	}
//}

bool ChessBoard::CanCastle( const ChessManMoveInfo& info ) const
{
	if(m_CurrPlayer == White)
	{
		if(info.y == 1)
		{
			if(m_WhiteKingSide==Impossible)
				return false;
			if(m_Board[0+RowBeginIndex][5+ColBeginIndex] != Empty || m_Board[0+RowBeginIndex][6+ColBeginIndex] != Empty)
				return false;
		}
		else if(info.y == -1)
		{
			if(m_WhiteQueenSide==Impossible)
				return false;
			if(m_Board[0+RowBeginIndex][2+ColBeginIndex] != Empty 
				|| m_Board[0+RowBeginIndex][3+ColBeginIndex] != Empty
				|| m_Board[0+RowBeginIndex][1+ColBeginIndex] != Empty)
				return false;
		}
	}
	else
	{
		if(info.y == 1)
		{
			if(m_BlackKingSide==Impossible)
				return false;
			if(m_Board[7+RowBeginIndex][5+ColBeginIndex] != Empty || m_Board[7+RowBeginIndex][6+ColBeginIndex] != Empty)
				return false;
		}
		else if(info.y == -1)
		{
			if(m_BlackQueenSide==Impossible)
				return false;
			if(m_Board[7+RowBeginIndex][2+ColBeginIndex] != Empty 
				|| m_Board[7+RowBeginIndex][3+ColBeginIndex] != Empty
				|| m_Board[7+RowBeginIndex][1+ColBeginIndex] != Empty )
				return false;
		}
	}


	ChessManMoveInfo moveInfo;
	BoardCoord dest;
	int destChessMan;

	std::queue<ChessManMoveInfo> moveInfoQueue;
	FindAllLegalMove(moveInfoQueue,-m_CurrPlayer);

	while(!moveInfoQueue.empty())
	{
		moveInfo = moveInfoQueue.front();
		moveInfoQueue.pop();

		dest.x = moveInfo.chessManCoord.x + moveInfo.x * moveInfo.step;
		dest.y = moveInfo.chessManCoord.y + moveInfo.y * moveInfo.step;

		//set the check state
		destChessMan = GetChessManType(dest);
		if( destChessMan*m_CurrPlayer == King )
		{
			//check, all the castle is disable
			return false;
		}//end if

		if(m_CurrPlayer==White)
		{
			if(dest.x !=0)
				continue ;
			if(info.y == 1 && (dest.y==5 || dest.y==6) )
				return false;
			else if(info.y == -1 && (dest.x==2 || dest.y==3))
				return false;
		}
		else
		{
			if(dest.x !=7)
				continue ;
			if(info.y == 1 && (dest.y==5 || dest.y==6) )
				return false;
			else if(info.y == -1 && (dest.x==2 || dest.y==3))
				return false;
		}
	}//end while

	return true;
}

void ChessBoard::SetCastleState( const ChessManMoveInfo& info )
{
	if(info.chessManCoord.x == 0 && info.chessManCoord.y == 0)
	{
		m_WhiteQueenSide = Impossible;
	}
	else if(info.chessManCoord.x ==0 && info.chessManCoord.y == 7)
	{
		m_WhiteKingSide = Impossible;
	}
	else if(info.chessManCoord.x == 7 && info.chessManCoord.y == 0)
	{
		m_BlackQueenSide = Impossible;
	}
	else if(info.chessManCoord.x == 7 && info.chessManCoord.y == 7)
	{
		m_BlackKingSide = Impossible;
	}
	else if(info.chessManCoord.x == 0 && info.chessManCoord.y == 4)
	{
		m_WhiteKingSide = Impossible;
		m_WhiteQueenSide = Impossible;
	}
	else if(info.chessManCoord.x ==7 && info.chessManCoord.y == 4)
	{
		m_BlackKingSide = Impossible;
		m_BlackQueenSide = Impossible;
	}
}